package com.ale.oxetool.util;

import com.ale.oxetool.model.BatchSwitchTask;
import com.ale.oxetool.model.SwitchTask;
import com.ale.oxetool.service.PhoneService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static com.ale.oxetool.model.SwitchTask.*;

/**
 * Created by guoshunw on 2016/4/4.
 */
public class SwitchTaskWorker implements Runnable {
    private BatchSwitchTask batchSwitchTask;
    private PhoneService phoneService;

    private int batchSize;
    private int delay; // seconds

    private int delayBeforeSetOperation;

    private static Logger log = LoggerFactory.getLogger(SwitchTaskWorker.class);
    private ExecutorService pool = null;

    public SwitchTaskWorker(BatchSwitchTask batchSwitchTask, PhoneService phoneService, int batchSize, int interval, int delayBeforeSetOperation) {
        this.batchSwitchTask = batchSwitchTask;
        this.phoneService = phoneService;
        if (batchSize > 0) {
            pool = Executors.newScheduledThreadPool(batchSize);
        }
        this.batchSize = batchSize;
        this.delay = interval;
        this.delayBeforeSetOperation = delayBeforeSetOperation;

    }

    @Override
    public void run() {
        log.info("=================Start batch switch task thread: {} begin ==================", Thread.currentThread().getName());

        List<SwitchTask> unFinishedTasks = batchSwitchTask.getTasks().stream().filter(task -> DONE != task.getStatus()).collect(Collectors.toList());
        //1. open telnet for phones that in un-finished tasks
        String result = phoneService.openTelnetForPhones(unFinishedTasks.stream().map(SwitchTask::getPhone).collect(Collectors.toList()));
        log.debug("Open phones telnet result: {}", result);

        // waiting several seconds for oxe to open the phones telnet ports
        try {
            TimeUnit.SECONDS.wait(delayBeforeSetOperation);
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("Sleep interrupted");
        }

        if (batchSize <= 0 || null == pool) {
            log.info("Switch phone in a serial.");
            for (SwitchTask task : unFinishedTasks) {
                switchPhone(task);
            }
            log.debug("=================== Submit task thread: {} end ==================", Thread.currentThread().getName());
            return;
        }

        // the executor service pool is only initialized when the batchSize greater than zero
        int batchNum = (int) Math.ceil(batchSwitchTask.size() / (double) batchSize);
        log.info("Parallel switch phone with batch size {}, batch number={}.", batchSize, batchNum);
        for (int i = 0; i < batchNum; ++i) {
            if (batchSize >= batchSwitchTask.size()) {
                unFinishedTasks = batchSwitchTask.getTasks();
                log.debug("Start only 1 batch because batch size is {} great than the tasks number {} to be started.", batchSize, batchSwitchTask.size());
            } else {
                int startIndex = i * batchSize;
                int endIndex = Math.min(startIndex + batchSize, batchSwitchTask.size());
                unFinishedTasks = batchSwitchTask.getTasks().subList(startIndex, endIndex);
                log.debug("Start batch {}({}-{}) of {} not finished task", i, startIndex, endIndex, batchSwitchTask.size());
            }

            List<Callable<String>> unFinishedTasksCallable = unFinishedTasks.stream()
                    .map(task -> (Callable<String>) () -> switchPhone(task)).collect(Collectors.toList());

            try {
                List<Future<String>> results = pool.invokeAll(unFinishedTasksCallable);
                //subList has been update status
                results.forEach(f -> {
                    try {
//                      wait for submit switch task thread done.
                        String r = f.get();
                        log.debug("Unfinished future return string: {}", r);
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                });
                batchSwitchTask.updateCurrentStatus();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                //sleep delay seconds
                log.debug("Delay {} seconds before next batch start...", delay);
                TimeUnit.SECONDS.sleep(delay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        pool.shutdownNow();
        log.info("=================== Submit task thread: {} end ==================", Thread.currentThread().getName());
    }

    private String switchPhone(SwitchTask task) {
        String result;
        try {
            result = phoneService.switchPhoneWithoutOpenPhoneTelnet(task.getPhone());
            log.debug("Switch phone {} result {}", task.getPhone(), result);
            task.setStatus(IN_PROGRESS);
            result = "inProgress";
        } catch (IOException e) {
            log.error("Phone {} switch fail, details: {}", task.getPhone(), e.getMessage());
            task.setStatus(FAIL);
            e.printStackTrace();
            result = "fail";
        }
        return result;
    }
}